package org.dromara.system.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ijpay.core.enums.RequestMethodEnum;
import com.ijpay.core.kit.PayKit;
import com.ijpay.core.kit.WxPayKit;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.exception.BusinessException;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.system.constant.MachineConstant;
import org.dromara.system.domain.SimUserPurse;
import org.dromara.system.domain.SimUserWithdrawalRecords;
import org.dromara.system.domain.WechatConfig;
import org.dromara.system.domain.bo.*;
import org.dromara.system.domain.vo.CallBackForWithdrawalVo;
import org.dromara.system.domain.vo.SimUserWithdrawalRecordsVo;
import org.dromara.system.domain.vo.WeChatTransferToChangeVo;
import org.dromara.system.enums.SysUserProfitRecordsEnum;
import org.dromara.system.enums.WithdrawalStatusEnum;
import org.dromara.system.mapper.SimUserWithdrawalRecordsMapper;
import org.dromara.system.service.ISimUserPurseRecordsService;
import org.dromara.system.service.ISimUserPurseService;
import org.dromara.system.service.ISimUserWithdrawalRecordsService;
import org.dromara.system.service.IWechatConfigService;
import org.dromara.system.service.impl.permission.PermissionBaseService;
import org.dromara.system.util.JsonUtil;
import org.dromara.system.util.OrderUtil;
import org.springframework.stereotype.Service;

import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 提现记录Service业务层处理
 *
 * @author Lion Li
 * @date 2025-08-28
 */
@Slf4j
@RequiredArgsConstructor
@Service
public class SimUserWithdrawalRecordsServiceImpl extends ServiceImpl<SimUserWithdrawalRecordsMapper, SimUserWithdrawalRecords>
    implements ISimUserWithdrawalRecordsService {

    static final int TAG_LENGTH_BIT = 128;
    private static final String URL_SUFFIX = "/v3/fund-app/mch-transfer/transfer-bills";
    private static final String WECHAT_NOTIFY_URL = "https://sim.hbtuding.com/system/userWithdrawalRecords/wechatWithdrawalInitiation/callBack";
    private final PermissionBaseService permissionBaseService;
    private final ISimUserPurseService simUserPurseService;
    private final ISimUserPurseRecordsService simUserPurseRecordsService;
    private final IWechatConfigService wechatConfigService;

    /**
     * 查询提现记录
     *
     * @param id 主键
     * @return 提现记录
     */
    @Override
    public SimUserWithdrawalRecordsVo queryById(Long id) {
        return baseMapper.selectVoById(id);
    }

    /**
     * 分页查询提现记录列表
     *
     * @param bo        查询条件
     * @param pageQuery 分页参数
     * @return 提现记录分页列表
     */
    @Override
    public TableDataInfo<SimUserWithdrawalRecordsVo> queryPageList(SimUserWithdrawalRecordsBo bo, PageQuery pageQuery) {
        BusinessException.isNull(bo.getStatusTag(), "statusTag标识不能为空！");
        permissionBaseService.assignPermission(bo);
        IPage<SimUserWithdrawalRecordsVo> page = new Page<>(pageQuery.getPageNum(), pageQuery.getPageSize());
        IPage<SimUserWithdrawalRecordsVo> list = baseMapper.findAll(page, bo);
        List<SimUserWithdrawalRecordsVo> records = list.getRecords();
        if (CollUtil.isNotEmpty(records)) {
            records.forEach(t -> {
                t.setApprovalName(WithdrawalStatusEnum.findOne(t.getApprovalStatus()).getName());
            });
        }
        return TableDataInfo.build(list);
    }


    @Override
    public TableDataInfo<SimUserWithdrawalRecordsVo> listReception(SimUserWithdrawalRecordsBo bo, PageQuery pageQuery) {
        if (ObjectUtil.isNull(bo.getUserId())) {
            bo.setUserId(LoginHelper.getUserId());
        }
        IPage<SimUserWithdrawalRecordsVo> page = new Page<>(pageQuery.getPageNum(), pageQuery.getPageSize());
        IPage<SimUserWithdrawalRecordsVo> list = baseMapper.findAll(page, bo);
        List<SimUserWithdrawalRecordsVo> records = list.getRecords();
        if (CollUtil.isNotEmpty(records)) {
            records.forEach(t -> {
                t.setApprovalName(WithdrawalStatusEnum.findOne(t.getApprovalStatus()).getName());
            });
        }
        return TableDataInfo.build(list);
    }

    /**
     * 查询符合条件的提现记录列表
     *
     * @param bo 查询条件
     * @return 提现记录列表
     */
    @Override
    public List<SimUserWithdrawalRecordsVo> queryList(SimUserWithdrawalRecordsBo bo) {
        LambdaQueryWrapper<SimUserWithdrawalRecords> lqw = buildQueryWrapper(bo);
        return baseMapper.selectVoList(lqw);
    }

    private LambdaQueryWrapper<SimUserWithdrawalRecords> buildQueryWrapper(SimUserWithdrawalRecordsBo bo) {
        LambdaQueryWrapper<SimUserWithdrawalRecords> lqw = Wrappers.lambdaQuery();
        lqw.orderByAsc(SimUserWithdrawalRecords::getId);
        lqw.eq(bo.getUserId() != null, SimUserWithdrawalRecords::getUserId, bo.getUserId());
        lqw.eq(StringUtils.isNotBlank(bo.getNo()), SimUserWithdrawalRecords::getNo, bo.getNo());
        lqw.eq(StringUtils.isNotBlank(bo.getInsideNo()), SimUserWithdrawalRecords::getInsideNo, bo.getInsideNo());
        lqw.eq(bo.getAmount() != null, SimUserWithdrawalRecords::getAmount, bo.getAmount());
        lqw.eq(bo.getReceivedAmount() != null, SimUserWithdrawalRecords::getReceivedAmount, bo.getReceivedAmount());
        lqw.eq(bo.getIdentification() != null, SimUserWithdrawalRecords::getIdentification, bo.getIdentification());
        lqw.eq(StringUtils.isNotBlank(bo.getAlipayAccount()), SimUserWithdrawalRecords::getAlipayAccount, bo.getAlipayAccount());
        lqw.like(StringUtils.isNotBlank(bo.getAlipayUserName()), SimUserWithdrawalRecords::getAlipayUserName, bo.getAlipayUserName());
        lqw.eq(StringUtils.isNotBlank(bo.getUserBankCardNumber()), SimUserWithdrawalRecords::getUserBankCardNumber, bo.getUserBankCardNumber());
        lqw.eq(bo.getApprovalStatus() != null, SimUserWithdrawalRecords::getApprovalStatus, bo.getApprovalStatus());
        lqw.eq(bo.getApprovalTime() != null, SimUserWithdrawalRecords::getApprovalTime, bo.getApprovalTime());
        return lqw;
    }

    /**
     * 新增提现记录
     *
     * @param bo 提现记录
     * @return 是否新增成功
     */
    @Override
    public Boolean insertByBo(SimUserWithdrawalRecordsBo bo) {
        SimUserWithdrawalRecords entity = MapstructUtils.convert(bo, SimUserWithdrawalRecords.class);
        BigDecimal amount = entity.getAmount();
        SimUserPurse sysUserPurse = simUserPurseService.lambdaQuery().eq(SimUserPurse::getUserId, entity.getUserId()).one();
        BusinessException.isTrue(NumberUtil.isGreater(amount, sysUserPurse.getProfit()), "提现金额大于钱包余额！");
        //0：支付宝 1：银行卡  2微信
        Integer identification = entity.getIdentification();
        if (ObjectUtil.equal(identification, MachineConstant.NUMBER.ZERO)) {
            BusinessException.isTrue(StrUtil.isBlank(entity.getAlipayAccount())
                || StrUtil.isBlank(entity.getAlipayUserName()), "支付宝账号，支付宝姓名不能为空！");
        } else if (ObjectUtil.equal(identification, MachineConstant.NUMBER.ONE)) {
            BusinessException.isTrue(Boolean.TRUE, "银行卡提现暂未开通！");
        } else if (ObjectUtil.equal(identification, MachineConstant.NUMBER.TWO)) {
            log.info("微信发起提现");
        } else {
            BusinessException.isTrue(Boolean.TRUE, "提现渠道错误！");
        }
        //计算到账金额
        BusinessException.isTrue(NumberUtil.isLessOrEqual(amount, BigDecimal.ZERO), "到账金额小于等于0！");
        //钱包扣钱
        SimUserPurseBo sysUserPurseBo = SimUserPurseBo.builder()
            .userId(entity.getUserId())
            .type(SysUserProfitRecordsEnum.WITHDRAWAL_POINTS.getCode())
            .profit(NumberUtil.mul(amount, MachineConstant.NUMBER.ONE_NEGATIVE))
            .build();
        simUserPurseRecordsService.updateUserPurse(CollUtil.newArrayList(sysUserPurseBo));
        entity.setReceivedAmount(amount);
        String orderNo = OrderUtil.getOrderNo("TD", 6);
        String insideNo = orderNo + RandomUtil.randomString(5);
        entity.setNo(orderNo);
        entity.setInsideNo(insideNo);
        entity.setApprovalStatus(WithdrawalStatusEnum.PENDING_APPROVAL.getCode());
        boolean flag = baseMapper.insert(entity) > 0;
        if (flag) {
            bo.setId(entity.getId());
        }
        return flag;
    }

    /**
     * 修改提现记录
     *
     * @param bo 提现记录
     * @return 是否修改成功
     */
    @Override
    public Boolean updateByBo(SimUserWithdrawalRecordsBo bo) {
        SimUserWithdrawalRecords update = MapstructUtils.convert(bo, SimUserWithdrawalRecords.class);
        validEntityBeforeSave(update);
        return baseMapper.updateById(update) > 0;
    }

    /**
     * 保存前的数据校验
     */
    public void validEntityBeforeSave(SimUserWithdrawalRecords entity) {
        BigDecimal amount = entity.getAmount();
        SimUserPurse sysUserPurse = simUserPurseService.lambdaQuery().eq(SimUserPurse::getUserId, entity.getUserId()).one();
        BusinessException.isTrue(NumberUtil.isGreater(amount, sysUserPurse.getProfit()), "提现金额大于钱包余额！");
        //0：支付宝 1：银行卡  2微信
        Integer identification = entity.getIdentification();
        if (ObjectUtil.equal(identification, MachineConstant.NUMBER.ZERO)) {
            BusinessException.isTrue(StrUtil.isBlank(entity.getAlipayAccount())
                || StrUtil.isBlank(entity.getAlipayUserName()), "支付宝账号，支付宝姓名不能为空！");
        } else if (ObjectUtil.equal(identification, MachineConstant.NUMBER.ONE)) {
            BusinessException.isTrue(Boolean.TRUE, "银行卡提现暂未开通！");
        } else if (ObjectUtil.equal(identification, MachineConstant.NUMBER.TWO)) {
            log.info("微信发起提现");
        } else {
            BusinessException.isTrue(Boolean.TRUE, "提现渠道错误！");
        }
        //计算到账金额
        BusinessException.isTrue(NumberUtil.isLessOrEqual(amount, BigDecimal.ZERO), "到账金额小于等于0！");
        //钱包扣钱
        SimUserPurseBo sysUserPurseBo = SimUserPurseBo.builder()
            .userId(entity.getUserId())
            .type(SysUserProfitRecordsEnum.WITHDRAWAL_POINTS.getCode())
            .profit(NumberUtil.mul(amount, MachineConstant.NUMBER.ONE_NEGATIVE))
            .build();
        simUserPurseRecordsService.updateUserPurse(CollUtil.newArrayList(sysUserPurseBo));
        entity.setReceivedAmount(amount);
        String orderNo = OrderUtil.getOrderNo("TD", 6);
        String insideNo = orderNo + RandomUtil.randomString(5);
        entity.setNo(orderNo);
        entity.setInsideNo(insideNo);
        entity.setApprovalStatus(WithdrawalStatusEnum.PENDING_APPROVAL.getCode());
    }

    /**
     * 校验并批量删除提现记录信息
     *
     * @param ids     待删除的主键集合
     * @param isValid 是否进行有效性校验
     * @return 是否删除成功
     */
    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if (isValid) {
            //TODO 做一些业务上的校验,判断是否需要校验
        }
        return baseMapper.deleteByIds(ids) > 0;
    }

    @Override
    public WeChatTransferToChangeVo wechatWithdrawalInitiation(SimUserWithdrawalRecordsBo bo) {
        log.info("wechatWithdrawalInitiation=={}", JSONUtil.toJsonStr(bo));
        String openId = bo.getOpenId();
        BusinessException.isTrue(StrUtil.isBlank(openId), "用户OPENID不能为空！");
        SimUserWithdrawalRecords entity = MapstructUtils.convert(bo, SimUserWithdrawalRecords.class);
        BigDecimal amount = entity.getAmount();
        SimUserPurse sysUserPurse = simUserPurseService.lambdaQuery().eq(SimUserPurse::getUserId, entity.getUserId()).one();
        BusinessException.isTrue(NumberUtil.isGreater(amount, sysUserPurse.getProfit()), "提现金额大于钱包余额！");

        String orderNo = OrderUtil.getOrderNo("TD", 6);
        String insideNo = orderNo + RandomUtil.randomString(5);
        //拉起微信支付
        WechatConfig wechatConfig = wechatConfigService.lambdaQuery().eq(WechatConfig::getUserId, MachineConstant.ADMIN).one();
        BusinessException.isNull(wechatConfig, "代理商微信相关参数未配置！");
        WechatTransferBo transferBo = WechatTransferBo.builder()
            .wechatConfig(wechatConfig)
            .receivedAmount(amount)
            .openId(openId)
            .orderNo(orderNo)
            .insideNo(insideNo)
            .build();
        String resultBody = wechatTransfer(transferBo);
        log.info("出参json:{}", resultBody);
        if (StrUtil.contains(resultBody, "code")) {
            Map<Object, Object> map = JsonUtil.toMap(resultBody);
            String message = Convert.toStr(map.get("message"));
            BusinessException.isTrue(Boolean.TRUE, message);
        } else {
            //扣掉用户的钱
            WeChatTransferToChangeVo weChatTransferToChangeRspDTO = JSONUtil.toBean(resultBody, WeChatTransferToChangeVo.class);
            weChatTransferToChangeRspDTO.setAppId(wechatConfig.getAppId());
            weChatTransferToChangeRspDTO.setMchId(wechatConfig.getMchId());
            //钱包扣钱
            SimUserPurseBo sysUserPurseBo = SimUserPurseBo.builder()
                .userId(entity.getUserId())
                .type(SysUserProfitRecordsEnum.WITHDRAWAL_POINTS.getCode())
                .profit(NumberUtil.mul(amount, MachineConstant.NUMBER.ONE_NEGATIVE))
                .build();
            simUserPurseRecordsService.updateUserPurse(CollUtil.newArrayList(sysUserPurseBo));
            entity.setNo(orderNo);
            entity.setReceivedAmount(amount);
            entity.setApprovalStatus(WithdrawalStatusEnum.TO_BE_CLAIMED_WITHDRAWAL.getCode());
            baseMapper.insert(entity);
            return weChatTransferToChangeRspDTO;
        }
        return null;
    }

    @Override
    public String wechatWithdrawalInitiationCall(HashMap<String, Object> map) {
        log.info("callBackForWithdrawal回调返回{}", JsonUtil.toJSONString(map));
        HashMap<String, Object> resultMap = new HashMap<>();
        try {
            Map<Object, Object> resource = JsonUtil.toMap(map.get("resource"));
            String decryptToString = decryptToString(Convert.toStr(resource.get("associated_data")).getBytes()
                , Convert.toStr(resource.get("nonce")).getBytes()
                , Convert.toStr(resource.get("ciphertext")));
            CallBackForWithdrawalVo callBackForWithdrawalVo = JsonUtil.toObject(decryptToString, CallBackForWithdrawalVo.class);
            log.info("微信转账订单号{},out_bill_no={}", callBackForWithdrawalVo.getOut_bill_no(), JsonUtil.toJSONString(callBackForWithdrawalVo));
            //查询该订单是否已经提现成功
            SimUserWithdrawalRecords merWithdrawalRecords = lambdaQuery()
                .eq(SimUserWithdrawalRecords::getInsideNo, callBackForWithdrawalVo.getOut_bill_no()).one();
            log.info("查询到的订单记录是{}", JsonUtil.toJSONString(merWithdrawalRecords));
            if (ObjectUtil.isNull(merWithdrawalRecords)) {
                resultMap.put("code", "SUCCESS");
                resultMap.put("message", "");
                return JsonUtil.toJSONString(resultMap);
            }
            //如果转账成功则扣掉用户钱包的钱
            if (StrUtil.equals(callBackForWithdrawalVo.getState(), "SUCCESS")) {
                //增加提现记录
                lambdaUpdate()
                    .set(SimUserWithdrawalRecords::getApprovalStatus, WithdrawalStatusEnum.SUCCESSFUL_WITHDRAWAL.getCode())
                    .eq(SimUserWithdrawalRecords::getId, merWithdrawalRecords.getId())
                    .update();
            }
            resultMap.put("code", "SUCCESS");
            resultMap.put("message", "");
        } catch (Exception e) {
            log.info("微信转账失败，原因是{}", e.getMessage());
            resultMap.put("code", "FAILED");
            resultMap.put("message", "");
        }
        return JsonUtil.toJSONString(resultMap);
    }

    @Override
    public Boolean withdrawalApproval(WithdrawalApprovalBo bo) {
        SimUserWithdrawalRecords sysUserWithdrawal = lambdaQuery()
            .eq(SimUserWithdrawalRecords::getId, bo.getId()).one();
        BusinessException.isTrue(!ObjectUtil.equal(sysUserWithdrawal.getApprovalStatus(), WithdrawalStatusEnum.PENDING_APPROVAL.getCode()),
            "该订单不为待审核状态，请检查！");
        if (ObjectUtil.equal(bo.getStatus(), MachineConstant.NUMBER.ONE)) {
            //驳回 钱包加钱
            SimUserPurseBo sysUserPurseBo = SimUserPurseBo.builder()
                .userId(sysUserWithdrawal.getUserId())
                .type(SysUserProfitRecordsEnum.WITHDRAWAL_REJECTED_POINTS.getCode())
                .profit(sysUserWithdrawal.getAmount())
                .build();
            simUserPurseRecordsService.updateUserPurse(CollUtil.newArrayList(sysUserPurseBo));
            lambdaUpdate()
                .set(SimUserWithdrawalRecords::getApprovalStatus, WithdrawalStatusEnum.REJECT_WITHDRAWAL.getCode())
                .set(SimUserWithdrawalRecords::getApprovalTime, DateUtil.date())
                .eq(SimUserWithdrawalRecords::getId, bo.getId()).update();
            return Boolean.TRUE;
        } else {
            //同意
            Integer identification = sysUserWithdrawal.getIdentification();
            if (ObjectUtil.equal(identification, MachineConstant.NUMBER.ZERO)) {
                //支付宝
                BusinessException.isTrue(Boolean.TRUE, "暂未开通支付宝转账！");
            } else if (ObjectUtil.equal(identification, MachineConstant.NUMBER.ONE)) {
                //银行卡
                BusinessException.isTrue(Boolean.TRUE, "暂未开通银行卡转账！");
            } else if (ObjectUtil.equal(identification, MachineConstant.NUMBER.TWO)) {
                //微信
                lambdaUpdate()
                    .set(SimUserWithdrawalRecords::getApprovalStatus, WithdrawalStatusEnum.TO_BE_CLAIMED_WITHDRAWAL.getCode())
                    .set(SimUserWithdrawalRecords::getApprovalTime, DateUtil.date())
                    .eq(SimUserWithdrawalRecords::getId, bo.getId()).update();
            } else {
                BusinessException.isTrue(Boolean.TRUE, "提现渠道异常！");
            }
        }
        return Boolean.TRUE;
    }

    @Override
    public WeChatTransferToChangeVo claimWithdrawal(ClaimWechatWithdrawalBo bo) {
        SimUserWithdrawalRecords merWithdrawalRecords = lambdaQuery()
            .eq(SimUserWithdrawalRecords::getId, bo.getId()).one();
        BusinessException.isNull(merWithdrawalRecords, "提现订单号不存在");
        BusinessException.isTrue(ObjectUtil.notEqual(merWithdrawalRecords.getApprovalStatus(), WithdrawalStatusEnum.TO_BE_CLAIMED_WITHDRAWAL.getCode())
            , "订单不为待领取状态");
        WechatConfig wechatConfig = wechatConfigService.lambdaQuery().eq(WechatConfig::getUserId, MachineConstant.ADMIN).one();
        BusinessException.isNull(wechatConfig, "代理商微信相关参数未配置！");
        String no = merWithdrawalRecords.getNo();
        //更换内部订单号
        String insideNo = no + RandomUtil.randomString(5);
        merWithdrawalRecords.setInsideNo(insideNo);
        lambdaUpdate()
            .set(SimUserWithdrawalRecords::getInsideNo, insideNo)
            .eq(SimUserWithdrawalRecords::getId, merWithdrawalRecords.getId())
            .update();
        WechatTransferBo transferBo = WechatTransferBo.builder()
            .wechatConfig(wechatConfig)
            .receivedAmount(merWithdrawalRecords.getReceivedAmount())
            .openId(bo.getOpenId())
            .orderNo(merWithdrawalRecords.getNo())
            .insideNo(merWithdrawalRecords.getInsideNo())
            .build();
        String resultBody = wechatTransfer(transferBo);
        if (StrUtil.contains(resultBody, "code")) {
            Map<Object, Object> map = JsonUtil.toMap(resultBody);
            String message = Convert.toStr(map.get("message"));
            BusinessException.isTrue(Boolean.TRUE, message);
        } else {
            WeChatTransferToChangeVo weChatTransferToChangeRspDTO = JSONUtil.toBean(resultBody, WeChatTransferToChangeVo.class);
            weChatTransferToChangeRspDTO.setAppId(wechatConfig.getAppId());
            weChatTransferToChangeRspDTO.setMchId(wechatConfig.getMchId());
            return weChatTransferToChangeRspDTO;
        }
        return null;
    }

    @Override
    public List<HashMap<String, Object>> queryWithdrawalStatusEnums() {
        return Arrays.stream(WithdrawalStatusEnum.values())
            .map(t -> {
                HashMap<String, Object> hashMap = new HashMap<>();
                hashMap.put("approvalStatus", t.getCode());
                hashMap.put("approvalStatusName", t.getName());
                return hashMap;
            }).collect(Collectors.toList());
    }

    public String wechatTransfer(WechatTransferBo bo) {
        WechatConfig wechatConfig = bo.getWechatConfig();
        Integer withdrawalAmount = Convert.toInt(NumberUtil.mul(bo.getReceivedAmount(), 100));
        long timestamp = System.currentTimeMillis() / 1000;
        String authType = "WECHATPAY2-SHA256-RSA2048";
        String nonceStr = WxPayKit.generateStr();
        //发起转账的商户号
        String mchId = wechatConfig.getMchId();
        //非对称加密，公钥证书的证书序列号
        String serialNo = wechatConfig.getGzhSerialNumber();
        //非对称加密的私钥  pem
        String privateKey = wechatConfig.getGzhPriKey();
        //转账业务信息
        JSONObject rootJSON = new JSONObject();
        rootJSON.put("appid", wechatConfig.getAppId());
        //商户单号，随机生成，但需要确保系统内唯一。
        rootJSON.put("out_bill_no", bo.getInsideNo());
        //转账场景id，默认值1000，代表：现金营销。可以到微信支付后台申请开通其他场景
        rootJSON.put("transfer_scene_id", "1000");
        rootJSON.put("openid", bo.getOpenId());
        //转账金额，单位：分
        rootJSON.put("transfer_amount", withdrawalAmount);
        rootJSON.put("transfer_remark", "分销");
        rootJSON.put("notify_url", WECHAT_NOTIFY_URL);
        //转账活动备注
        List<JSONObject> childList = getTransferSceneReportInfos();
        rootJSON.put("transfer_scene_report_infos", childList);
        String body = JSONUtil.toJsonStr(rootJSON);
        log.info("入参json:{}", body);
        // 调用IJPay框架的工具类生成待签名参数
        String buildSignMessage = PayKit.buildSignMessage(RequestMethodEnum.POST, URL_SUFFIX, timestamp, nonceStr, body);
        //调用IJPay框架的工具类生成签名
        String signature = null;
        try {
            signature = PayKit.createSign(buildSignMessage, PayKit.getPrivateKey(privateKey));
        } catch (Exception e) {
            BusinessException.isTrue(Boolean.TRUE, "拉起微信转账失败！");
        }
        log.info("signature:{}", signature);
        // 调用IJPay框架的工具类，根据平台规则生成请求头 authorization
        String authorization = PayKit.getAuthorization(mchId, serialNo, nonceStr, String.valueOf(timestamp), signature, authType);
        log.info("Authorization:{}", authorization);
        //完整的"商家转账到零钱"的请求链接
        String url = "https://api.mch.weixin.qq.com/v3/fund-app/mch-transfer/transfer-bills";
        //调用Hutool包的工具类发起post请求
        HttpResponse httpResponse = HttpRequest.post(url)
            .header("Authorization", authorization)
            .header("Accept", "application/json")
            .header("Wechatpay-Serial", wechatConfig.getGzhSerialNumber())
            .header("Content-Type", "application/json")
            .body(body)
            .execute();
        return httpResponse.body();
    }


    public String decryptToString(byte[] associatedData, byte[] nonce, String ciphertext)
        throws GeneralSecurityException, IOException {
        try {
            Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
            SecretKeySpec key = new SecretKeySpec("fd92bed772bff671a17f56811f111731".getBytes(), "AES");
            GCMParameterSpec spec = new GCMParameterSpec(TAG_LENGTH_BIT, nonce);
            cipher.init(Cipher.DECRYPT_MODE, key, spec);
            cipher.updateAAD(associatedData);
            return new String(cipher.doFinal(Base64.getDecoder().decode(ciphertext)), StandardCharsets.UTF_8);
        } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
            throw new IllegalStateException(e);
        } catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
            throw new IllegalArgumentException(e);
        }
    }

    private List<JSONObject> getTransferSceneReportInfos() {
        List<JSONObject> childList = new ArrayList<>();
        JSONObject childJSON1 = new JSONObject();
        //注意，注意，注意！！这个info_type的值不能改，只能写：活动名称，否则就会调用失败。
        childJSON1.put("info_type", "活动名称");
        childJSON1.put("info_content", "支付好礼");

        JSONObject childJSON2 = new JSONObject();
        //这个也一样。这个info_type的值也不能改，只能写：奖励说明，否则就会调用失败。
        childJSON2.put("info_type", "奖励说明");
        childJSON2.put("info_content", "支付好礼的奖励说明");
        childList.add(childJSON1);
        childList.add(childJSON2);
        return childList;
    }

}
